package ControllerLayer.ControlServices.Segmenter;

import DataLayer.ImageDataContainer.ImageComponentsInfo.IImageComponentsInfo;
import DataLayer.ImageDataContainer.ImageInfo.IImageInfo;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.ArrayList;


public class LineSegmenter implements ISegmentUpdater{
	private BufferedImage buffImage;
        private int[] hist;   // histogram
        private ArrayList<Integer> localMins;  // list to store the local minimums
        private ArrayList<Point>   localMinsPoint;
        private ArrayList<Integer>   rows;

        /**
         * constructor
         */
	public LineSegmenter(){
	}

        /**
         * segmentates the image to lines and updates the imgCompInfo
         * @param imgCompInfo  file containing segmentation information
         * @param img  Image to segmentate
         */
	public void segmentLines(IImageInfo imgInfo){
            buffImage = imgInfo.getBWImage();
            makeHistogram();
            findMin();
	}

        /**
         * updates the file containing the segmentation information by calling
         * the addLines method of imgCompInfo
         * @param imgCompInfo
         */
	public void updateImageCompInfo(IImageComponentsInfo imgCompInfo)
        {
            imgCompInfo.addLines(localMinsPoint);
	}

         /**
         * makes the row-based histogram of buffImage
         */
        private void makeHistogram(){
            int height = buffImage.getHeight();
          //  System.out.println("height: "+height);
            int width = buffImage.getWidth();
          //  System.out.println("width: "+width);
            hist = new int[height];

            // counts the black pixels in each row
            for (int i = 0; i<height; ++i){
                int sum = 0;
                for(int j = 0; j<width; ++j){
                    int c = buffImage.getRGB(j, i);
                    // condition based on which we count the pixels
                    if(c != -1)
                        ++sum;
                    // getting the R G B components
                //    int  red = (c & 0x00ff0000) >> 16;
                //    int  green = (c & 0x0000ff00) >> 8;
                //    int  blue = c & 0x000000ff;

                 //   System.out.println(i+","+j+": "+c);
                 //   System.out.println(i+","+j+": "+red+" "+green+" "+blue);
                }
                hist[i] = sum;
            }

           // for (int i = 0; i<height; ++i)
            //    System.out.println(i+": "+hist[i]);

        }

        /**
         * finds the local minimums in the histogram, having their value less than
         * epsilon
         * it only works properly if the rows are clearly separated and there is
         * no writing in the first and last pixel row of the picture
         * @param epsilon
         * @return array with the minimum pints
         */
        private void findMin(){

            int n = hist.length;

            int sum = 0;
            int k = 0;   // counting the rows having at least one coloured pixel
            for(int i = 0; i<n; ++i){
                sum = sum + hist[i];
                if(hist[i] > 0)
                    ++k;
            }

            // the average number of black pixels in a row containing writing:
            int avg = sum/k;

            double epsilon = avg * 0.8;

            int i = 0;
            localMins = new ArrayList<Integer>();

            while(i<n){

                // finding the potential beginning of a row:
                while((i<n) &&(hist[i]<epsilon))
                    ++i;
                localMins.add(i);

                // finding the end of the upper row:
                while((i<n) && (hist[i]>=epsilon))
                    ++i;
                localMins.add(i);
                
            }

            if(localMins.size() % 2 == 1)
                localMins.add(n-1);

            double avgRow = 0;
            k = localMins.size();
            for(i = 0; i<k; i+=2)
                avgRow = avgRow + localMins.get(i+1) - localMins.get(i);

            avgRow = avgRow / (k/2);


            i = 0;
            boolean ok;
            
            while(i<localMins.size()-1){
                ok = true;
                //the row is not wide enough:
                if(localMins.get(i+1) - localMins.get(i) < avgRow*0.7){
                    localMins.remove(i);
                    localMins.remove(i);
                    ok = false;
                }
                
                if(ok)
                    i+=2;

                ////System.out.println(i);
            }

            k = localMins.size();
            rows = new ArrayList<Integer>();

            // finding the start of the forst row:
            i = localMins.get(0);
            while((i>0) && (hist[i]>avg*0.2))
                --i;

            rows.add(i);

            for(i = 1; i<k-2; i+=2)
                rows.add((localMins.get(i)+localMins.get(i+1))/2);

            // finding the end of the last row:
            i = localMins.get(k-1);
            while((i<n) && (hist[i]>avg*0.2))
                ++i;
            rows.add(i);

            makePoints();
        }


        /**
         * makes an array list of point out of the array list of integers
         */
        private void makePoints(){
            localMinsPoint = new ArrayList<Point>();

            ////System.out.println("Sorok: ");
            ////for(int i = 0; i<rows.size(); ++i)
                ////System.out.println(rows.get(i));
            
            int n = rows.size();
            int i = 0;
            while(i<n-1){
                Point p = new Point(rows.get(i), rows.get(i+1));
                localMinsPoint.add(p);
                i++;
            }

            ////System.out.println("Sorpontok: ");
            ////for(i = 0; i<localMinsPoint.size(); ++i)
                ////System.out.println(localMinsPoint.get(i));
        }


}
